#include "i2c_interface.h"
#include <stdlib.h>
#include <string.h>
#include "esp_log.h"

static const char* TAG = "i2c_interface.c";

#define LOGE(...)           ESP_LOGE(TAG, __VA_ARGS__)
#define LOGI(...)           ESP_LOGI(TAG, __VA_ARGS__)

#define READ_BIT            I2C_MASTER_READ
#define WRITE_BIT           I2C_MASTER_WRITE
#define ACK_CHECK_EN        I2C_MASTER_ACK
#define ACK_VAL             0
#define NACK_VAL        1
typedef struct _i2c_interface_device {
    i2c_interface_config config;
} i2c_interface_device_t;

int32_t i2c_interface_init(i2c_interface_device_t *device)
{
    int ret = ESP_OK;
    i2c_config_t conf;
    conf.mode = device->config.mode;
    conf.sda_io_num = device->config.sda_pin;
    conf.scl_io_num = device->config.scl_pin;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = 400 * 1000;
    conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;

    ret = i2c_param_config(device->config.i2c_num, &conf);
    if (ESP_OK != ret) {
        LOGE("i2c_param_config fail");
    } else {
        LOGI("i2c_param_config suc");
    }

    ret = i2c_driver_install(device->config.i2c_num, I2C_MODE_MASTER, 0, 0, 0);
    if (ESP_OK != ret) {
        LOGE("i2c_driver_install fail");
    } else {
        LOGI("i2c_driver_install suc");
    }
    return 0;
}

i2c_interface_handle_t i2c_interface_create(const i2c_interface_config *config)
{

    if (NULL == config) {
        return NULL;
    }

    i2c_interface_device_t *device = (i2c_interface_device_t *)malloc(sizeof(i2c_interface_device_t));
    if (NULL == device) {
        return NULL;
    }

    memcpy(&device->config, config, sizeof(i2c_interface_config));

    if (0 != i2c_interface_init(device)) {
        goto err;
    }

    return (i2c_interface_handle_t)device;
err:
    i2c_interface_destroy((i2c_interface_handle_t)device);
    return NULL;
}


int32_t i2c_interface_write_to_device(i2c_interface_handle_t handle, uint16_t addr, const void *reg_data, size_t reg_size, const uint8_t *data, uint32_t length)
{
    if (NULL == handle || NULL == data) {
        return -1;
    }

    if (length == 0) {
        return length;
    }
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | WRITE_BIT, ACK_CHECK_EN);
    if (reg_data && reg_size)
            i2c_master_write(cmd, (void *)reg_data, reg_size, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    i2c_interface_device_t *device = (i2c_interface_device_t *)handle;
    esp_err_t ret = i2c_master_cmd_begin(device->config.i2c_num, cmd, 2000 / portTICK_PERIOD_MS);

    i2c_cmd_link_delete(cmd);
    return ret == ESP_OK ? length : 0;
}


int32_t i2c_interface_read_from_device(i2c_interface_handle_t handle, uint16_t addr, const void *reg_data, size_t reg_size, uint8_t *data, uint32_t length)
{
    if (NULL == handle || NULL == data) {
        return -1;
    }

    if (length == 0) {
        return length;
    }

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
   if (reg_data && reg_size)
    {
        i2c_master_start(cmd);
        i2c_master_write_byte(cmd, addr << 1, true);
        i2c_master_write(cmd, (void *)reg_data, reg_size, true);
    }
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (addr << 1) | READ_BIT, true);
    i2c_master_read(cmd, data , length, I2C_MASTER_LAST_NACK);
    i2c_master_stop(cmd);

    i2c_interface_device_t *device = (i2c_interface_device_t *)handle;
    esp_err_t ret = i2c_master_cmd_begin(device->config.i2c_num, cmd, 2000 / portTICK_PERIOD_MS);

    i2c_cmd_link_delete(cmd);
    return ret == ESP_OK ? length : 0;
}

int32_t i2c_interface_destroy(i2c_interface_handle_t handle)
{
    if (NULL == handle) {
        return 0;
    }

    i2c_interface_device_t *device = (i2c_interface_device_t *)handle;

    i2c_driver_delete(device->config.i2c_num);
    free(device);
    return 0;
}
